use std::time::Duration;

use windows::Win32::{
	Foundation::{LPARAM, WPARAM},
	System::Performance::QueryPerformanceCounter,
	UI::{
		Input::KeyboardAndMouse::{GetKeyState, VIRTUAL_KEY},
		WindowsAndMessaging::{
			WHEEL_DELTA, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
			WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_XBUTTONDOWN, WM_XBUTTONUP,
		},
	},
};

use crate::{hiword, loword};

use super::{WinRef, Window};

pub struct WinEvent<'a>(pub &'a (), pub u32, pub WPARAM, pub LPARAM);
impl<'a> types::IntoEvt<Window, types::Mouse> for WinEvent<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Mouse> {
		fn other(wparam: usize) -> types::Button {
			let id = hiword(wparam as u32);
			let id = id % 255;
			let id = id as u8;
			types::Button::Mouse(id)
		}
		let msg = self.1;
		let wparam = self.2 .0;
		let lparam = self.3 .0;
		let (button, phase) = match msg {
			WM_LBUTTONDOWN => (types::Button::Left, types::Phase::Start),
			WM_LBUTTONUP => (types::Button::Left, types::Phase::End),
			WM_RBUTTONDOWN => (types::Button::Right, types::Phase::Start),
			WM_RBUTTONUP => (types::Button::Right, types::Phase::End),
			WM_MBUTTONDOWN => (types::Button::Middle, types::Phase::Start),
			WM_MBUTTONUP => (types::Button::Middle, types::Phase::End),
			WM_XBUTTONDOWN => (other(wparam), types::Phase::Start),
			WM_XBUTTONUP => (other(wparam), types::Phase::End),
			WM_MOUSEMOVE => (types::Button::Left, types::Phase::Update),
			_ => return None,
		};
		let point = (loword(lparam as _) as _, hiword(lparam as _) as _);
		let metas = conv_metas();
		let mut nanos = 0;
		_ = unsafe { QueryPerformanceCounter(&mut nanos) }; // TODO
		let time = Duration::from_nanos(nanos as _);
		Some(types::Mouse::new(button, phase, point, metas, time))
	}
}
impl<'a> types::IntoEvt<Window, types::Wheel> for WinEvent<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Wheel> {
		let msg = self.1;
		let wparam = self.2 .0;
		let value = (wparam >> 16) as i16;
		let value = value as f32 / WHEEL_DELTA as f32;
		match msg {
			WM_MOUSEWHEEL => types::Wheel::Line(0., value, 0.).into(),
			WM_MOUSEHWHEEL => types::Wheel::Line(value, 0., 0.).into(),
			_ => None,
		}
	}
}
impl<'a> types::IntoEvt<Window, types::Keyboard> for WinEvent<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Keyboard> {
		let msg = self.1;
		let wparam = self.2 .0;
		let lparam = self.3 .0;
		let state = match msg {
			WM_KEYDOWN => types::State::Down,
			WM_KEYUP => types::State::Up,
			WM_SYSKEYDOWN => types::State::Down,
			WM_SYSKEYUP => types::State::Up,
			_ => return None,
		};
		// https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
		// https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
		let vkey = loword(wparam as _);
		let _repeat = (lparam & 0xFFFF) as u16;
		let mut scancode = ((lparam >> 16) & 0xff) as u16;
		// The extended-key flag indicates whether the keystroke message originated from one of the additional keys on the Enhanced 101/102-key keyboard.
		// The extended keys consist of the ALT and CTRL keys on the right-hand side of the keyboard;
		// the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad;
		// the NUM LOCK key; the BREAK (CTRL+PAUSE) key;
		// the PRINT SCRN key;
		// and the divide (/) and ENTER keys in the numeric keypad.
		// The right-hand SHIFT key is not considered an extended-key, it has a separate scan code instead.
		//
		// If specified, the scan code consists of a sequence of two bytes, where the first byte has a value of 0xE0.
		let extended = ((lparam >> 24) & 0x01) != 0;
		if extended {
			scancode = 0xE000 | scancode;
		}
		// The context code indicates whether the ALT key was down when the keystroke message was generated.
		// The code is 1 if the ALT key was down and 0 if it was up.
		let _context_code = (lparam >> 29) & 0x01 != 0;
		// The previous key-state flag indicates whether the key that generated the keystroke message was previously up or down.
		// It is 1 if the key was previously down and 0 if the key was previously up.
		// You can use this flag to identify keystroke messages generated by the keyboard's automatic repeat feature.
		// This flag is set to 1 for WM_KEYDOWN and WM_SYSKEYDOWN keystroke messages generated by the automatic repeat feature.
		// It is always set to 1 for WM_KEYUP and WM_SYSKEYUP messages.
		let previous_state = (lparam >> 30) & 0x01 != 0;
		// The transition-state flag indicates whether pressing a key or releasing a key generated the keystroke message.
		// This flag is always set to 0 for WM_KEYDOWN and WM_SYSKEYDOWN messages;
		// it is always set to 1 for WM_KEYUP and WM_SYSKEYUP messages.
		let _transition_state = (lparam >> 31) & 0x01 != 0;

		let code = conv_code(scancode);
		let key = conv_key(vkey);
		let location = code.into();
		let metas = conv_metas();
		let repeat = match msg {
			WM_KEYDOWN => previous_state,
			WM_SYSKEYDOWN => previous_state,
			_ => false,
		};
		let composing = false;
		Some(types::Keyboard::new(key, code, state, location, metas, repeat, composing))
	}
}
impl<'a> types::IntoEvt<Window, types::Touch> for WinEvent<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Touch> {
		// TODO
		None
	}
}
fn conv_metas() -> types::Metas {
	use windows::Win32::UI::Input::KeyboardAndMouse as VK;
	fn key_pressed(vkey: VIRTUAL_KEY) -> bool {
		unsafe { (GetKeyState(vkey.0 as i32) & (1 << 15)) == (1 << 15) }
	}
	let mut metas = types::Metas::EMPTY;
	if key_pressed(VK::VK_SHIFT) {
		metas |= types::Metas::SHIFT;
	}
	if key_pressed(VK::VK_CONTROL) {
		metas |= types::Metas::CONTROL;
	}
	if key_pressed(VK::VK_MENU) {
		metas |= types::Metas::ALT;
	}
	if key_pressed(VK::VK_LWIN) || key_pressed(VK::VK_RWIN) {
		metas |= types::Metas::META;
	}
	metas
}
fn conv_code(code: u16) -> types::Code {
	// See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
	// and: https://www.w3.org/TR/uievents-code/
	// and: The widget/NativeKeyToDOMCodeName.h file in the firefox source
	use types::Code as KeyCode;
	match code {
		0x0029 => KeyCode::Backquote,
		0x002b => KeyCode::Backslash,
		0x000e => KeyCode::Backspace,
		0x001a => KeyCode::BracketLeft,
		0x001b => KeyCode::BracketRight,
		0x0033 => KeyCode::Comma,
		0x000b => KeyCode::Digit0,
		0x0002 => KeyCode::Digit1,
		0x0003 => KeyCode::Digit2,
		0x0004 => KeyCode::Digit3,
		0x0005 => KeyCode::Digit4,
		0x0006 => KeyCode::Digit5,
		0x0007 => KeyCode::Digit6,
		0x0008 => KeyCode::Digit7,
		0x0009 => KeyCode::Digit8,
		0x000a => KeyCode::Digit9,
		0x000d => KeyCode::Equal,
		0x0056 => KeyCode::IntlBackslash,
		0x0073 => KeyCode::IntlRo,
		0x007d => KeyCode::IntlYen,
		0x001e => KeyCode::KeyA,
		0x0030 => KeyCode::KeyB,
		0x002e => KeyCode::KeyC,
		0x0020 => KeyCode::KeyD,
		0x0012 => KeyCode::KeyE,
		0x0021 => KeyCode::KeyF,
		0x0022 => KeyCode::KeyG,
		0x0023 => KeyCode::KeyH,
		0x0017 => KeyCode::KeyI,
		0x0024 => KeyCode::KeyJ,
		0x0025 => KeyCode::KeyK,
		0x0026 => KeyCode::KeyL,
		0x0032 => KeyCode::KeyM,
		0x0031 => KeyCode::KeyN,
		0x0018 => KeyCode::KeyO,
		0x0019 => KeyCode::KeyP,
		0x0010 => KeyCode::KeyQ,
		0x0013 => KeyCode::KeyR,
		0x001f => KeyCode::KeyS,
		0x0014 => KeyCode::KeyT,
		0x0016 => KeyCode::KeyU,
		0x002f => KeyCode::KeyV,
		0x0011 => KeyCode::KeyW,
		0x002d => KeyCode::KeyX,
		0x0015 => KeyCode::KeyY,
		0x002c => KeyCode::KeyZ,
		0x000c => KeyCode::Minus,
		0x0034 => KeyCode::Period,
		0x0028 => KeyCode::Quote,
		0x0027 => KeyCode::Semicolon,
		0x0035 => KeyCode::Slash,
		0x0038 => KeyCode::AltLeft,
		0xe038 => KeyCode::AltRight,
		0x003a => KeyCode::CapsLock,
		0xe05d => KeyCode::ContextMenu,
		0x001d => KeyCode::ControlLeft,
		0xe01d => KeyCode::ControlRight,
		0x001c => KeyCode::Enter,
		0xe05b => KeyCode::MetaLeft,
		0xe05c => KeyCode::MetaRight,
		0x002a => KeyCode::ShiftLeft,
		0x0036 => KeyCode::ShiftRight,
		0x0039 => KeyCode::Space,
		0x000f => KeyCode::Tab,
		0x0079 => KeyCode::Convert,
		0x0072 => KeyCode::Lang1, // for non-Korean layout
		0xe0f2 => KeyCode::Lang1, // for Korean layout
		0x0071 => KeyCode::Lang2, // for non-Korean layout
		0xe0f1 => KeyCode::Lang2, // for Korean layout
		0x0070 => KeyCode::KanaMode,
		0x007b => KeyCode::NonConvert,
		0xe053 => KeyCode::Delete,
		0xe04f => KeyCode::End,
		0xe047 => KeyCode::Home,
		0xe052 => KeyCode::Insert,
		0xe051 => KeyCode::PageDown,
		0xe049 => KeyCode::PageUp,
		0xe050 => KeyCode::ArrowDown,
		0xe04b => KeyCode::ArrowLeft,
		0xe04d => KeyCode::ArrowRight,
		0xe048 => KeyCode::ArrowUp,
		0xe045 => KeyCode::NumLock,
		0x0052 => KeyCode::Numpad0,
		0x004f => KeyCode::Numpad1,
		0x0050 => KeyCode::Numpad2,
		0x0051 => KeyCode::Numpad3,
		0x004b => KeyCode::Numpad4,
		0x004c => KeyCode::Numpad5,
		0x004d => KeyCode::Numpad6,
		0x0047 => KeyCode::Numpad7,
		0x0048 => KeyCode::Numpad8,
		0x0049 => KeyCode::Numpad9,
		0x004e => KeyCode::NumpadAdd,
		0x007e => KeyCode::NumpadComma,
		0x0053 => KeyCode::NumpadDecimal,
		0xe035 => KeyCode::NumpadDivide,
		0xe01c => KeyCode::NumpadEnter,
		0x0059 => KeyCode::NumpadEqual,
		0x0037 => KeyCode::NumpadMultiply,
		0x004a => KeyCode::NumpadSubtract,
		0x0001 => KeyCode::Escape,
		0x003b => KeyCode::F1,
		0x003c => KeyCode::F2,
		0x003d => KeyCode::F3,
		0x003e => KeyCode::F4,
		0x003f => KeyCode::F5,
		0x0040 => KeyCode::F6,
		0x0041 => KeyCode::F7,
		0x0042 => KeyCode::F8,
		0x0043 => KeyCode::F9,
		0x0044 => KeyCode::F10,
		0x0057 => KeyCode::F11,
		0x0058 => KeyCode::F12,
		0x0064 => KeyCode::F13,
		0x0065 => KeyCode::F14,
		0x0066 => KeyCode::F15,
		0x0067 => KeyCode::F16,
		0x0068 => KeyCode::F17,
		0x0069 => KeyCode::F18,
		0x006a => KeyCode::F19,
		0x006b => KeyCode::F20,
		0x006c => KeyCode::F21,
		0x006d => KeyCode::F22,
		0x006e => KeyCode::F23,
		0x0076 => KeyCode::F24,
		0xe037 => KeyCode::PrintScreen,
		0x0054 => KeyCode::PrintScreen, // Alt + PrintScreen
		0x0046 => KeyCode::ScrollLock,
		0x0045 => KeyCode::Pause,
		0xe046 => KeyCode::Pause, // Ctrl + Pause
		0xe06a => KeyCode::BrowserBack,
		0xe066 => KeyCode::BrowserFavorites,
		0xe069 => KeyCode::BrowserForward,
		0xe032 => KeyCode::BrowserHome,
		0xe067 => KeyCode::BrowserRefresh,
		0xe065 => KeyCode::BrowserSearch,
		0xe068 => KeyCode::BrowserStop,
		0xe06b => KeyCode::LaunchApp1,
		0xe021 => KeyCode::LaunchApp2,
		0xe06c => KeyCode::LaunchMail,
		0xe022 => KeyCode::MediaPlayPause,
		0xe06d => KeyCode::MediaSelect,
		0xe024 => KeyCode::MediaStop,
		0xe019 => KeyCode::MediaTrackNext,
		0xe010 => KeyCode::MediaTrackPrevious,
		0xe05e => KeyCode::Power,
		0xe02e => KeyCode::AudioVolumeDown,
		0xe020 => KeyCode::AudioVolumeMute,
		0xe030 => KeyCode::AudioVolumeUp,
		_ => KeyCode::Unidentified(code),
	}
}
mod conv_key {
	use windows::Win32::{
		System::SystemServices::{LANG_JAPANESE, LANG_KOREAN},
		UI::Input::KeyboardAndMouse::{self as VK, GetKeyboardLayout},
	};

	use crate::loword;
	pub fn conv_key(vkey: u16) -> types::Key {
		let locale_id = unsafe { GetKeyboardLayout(0) }.0 as u64;
		let primary_lang_id = loword(locale_id as _) & 0x3ff;
		let is_korean = primary_lang_id as u32 == LANG_KOREAN;
		let is_japanese = primary_lang_id as u32 == LANG_JAPANESE;
		use types::Key;
		match VK::VIRTUAL_KEY(vkey) {
			VK::VK_LBUTTON => Key::Unidentified(vkey), // Mouse
			VK::VK_RBUTTON => Key::Unidentified(vkey), // Mouse

			// I don't think this can be represented with a Key
			VK::VK_CANCEL => Key::Unidentified(vkey),

			VK::VK_MBUTTON => Key::Unidentified(vkey),  // Mouse
			VK::VK_XBUTTON1 => Key::Unidentified(vkey), // Mouse
			VK::VK_XBUTTON2 => Key::Unidentified(vkey), // Mouse
			VK::VK_BACK => Key::Backspace,
			VK::VK_TAB => Key::Tab,
			VK::VK_CLEAR => Key::Clear,
			VK::VK_RETURN => Key::Enter,
			VK::VK_SHIFT => Key::Shift,
			VK::VK_CONTROL => Key::Control,
			VK::VK_MENU => Key::Alt,
			VK::VK_PAUSE => Key::Pause,
			VK::VK_CAPITAL => Key::CapsLock,

			// VK::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK::VK_HANGUL

			// VK::VK_HANGUL and VK::VK_KANA are defined as the same constant, therefore
			// we use appropriate conditions to differentiate between them
			VK::VK_HANGUL if is_korean => Key::HangulMode,
			VK::VK_KANA if is_japanese => Key::KanaMode,

			VK::VK_JUNJA => Key::JunjaMode,
			VK::VK_FINAL => Key::FinalMode,

			// VK::VK_HANJA and VK::VK_KANJI are defined as the same constant, therefore
			// we use appropriate conditions to differentiate between them
			VK::VK_HANJA if is_korean => Key::HanjaMode,
			VK::VK_KANJI if is_japanese => Key::KanjiMode,

			VK::VK_ESCAPE => Key::Escape,
			VK::VK_CONVERT => Key::Convert,
			VK::VK_NONCONVERT => Key::NonConvert,
			VK::VK_ACCEPT => Key::Accept,
			VK::VK_MODECHANGE => Key::ModeChange,
			VK::VK_SPACE => Key::Character(" ".into()),
			VK::VK_PRIOR => Key::PageUp,
			VK::VK_NEXT => Key::PageDown,
			VK::VK_END => Key::End,
			VK::VK_HOME => Key::Home,
			VK::VK_LEFT => Key::ArrowLeft,
			VK::VK_UP => Key::ArrowUp,
			VK::VK_RIGHT => Key::ArrowRight,
			VK::VK_DOWN => Key::ArrowDown,
			VK::VK_SELECT => Key::Select,
			VK::VK_PRINT => Key::Print,
			VK::VK_EXECUTE => Key::Execute,
			VK::VK_SNAPSHOT => Key::PrintScreen,
			VK::VK_INSERT => Key::Insert,
			VK::VK_DELETE => Key::Delete,
			VK::VK_HELP => Key::Help,

			VK::VK_0 => Key::Character("0".into()),
			VK::VK_1 => Key::Character("1".into()),
			VK::VK_2 => Key::Character("2".into()),
			VK::VK_3 => Key::Character("3".into()),
			VK::VK_4 => Key::Character("4".into()),
			VK::VK_5 => Key::Character("5".into()),
			VK::VK_6 => Key::Character("6".into()),
			VK::VK_7 => Key::Character("7".into()),
			VK::VK_8 => Key::Character("8".into()),
			VK::VK_9 => Key::Character("9".into()),

			VK::VK_A => Key::Character("a".into()),
			VK::VK_B => Key::Character("b".into()),
			VK::VK_C => Key::Character("c".into()),
			VK::VK_D => Key::Character("d".into()),
			VK::VK_E => Key::Character("e".into()),
			VK::VK_F => Key::Character("f".into()),
			VK::VK_G => Key::Character("g".into()),
			VK::VK_H => Key::Character("h".into()),
			VK::VK_I => Key::Character("i".into()),
			VK::VK_J => Key::Character("j".into()),
			VK::VK_K => Key::Character("k".into()),
			VK::VK_L => Key::Character("l".into()),
			VK::VK_M => Key::Character("m".into()),
			VK::VK_N => Key::Character("n".into()),
			VK::VK_O => Key::Character("o".into()),
			VK::VK_P => Key::Character("p".into()),
			VK::VK_Q => Key::Character("q".into()),
			VK::VK_R => Key::Character("r".into()),
			VK::VK_S => Key::Character("s".into()),
			VK::VK_T => Key::Character("t".into()),
			VK::VK_U => Key::Character("u".into()),
			VK::VK_V => Key::Character("v".into()),
			VK::VK_W => Key::Character("w".into()),
			VK::VK_X => Key::Character("x".into()),
			VK::VK_Y => Key::Character("y".into()),
			VK::VK_Z => Key::Character("z".into()),

			VK::VK_LWIN => Key::Super,
			VK::VK_RWIN => Key::Super,
			VK::VK_APPS => Key::ContextMenu,
			VK::VK_SLEEP => Key::Standby,

			// Numpad keys produce characters
			VK::VK_NUMPAD0 => Key::Character("0".into()),
			VK::VK_NUMPAD1 => Key::Character("1".into()),
			VK::VK_NUMPAD2 => Key::Character("2".into()),
			VK::VK_NUMPAD3 => Key::Character("3".into()),
			VK::VK_NUMPAD4 => Key::Character("4".into()),
			VK::VK_NUMPAD5 => Key::Character("5".into()),
			VK::VK_NUMPAD6 => Key::Character("6".into()),
			VK::VK_NUMPAD7 => Key::Character("7".into()),
			VK::VK_NUMPAD8 => Key::Character("8".into()),
			VK::VK_NUMPAD9 => Key::Character("9".into()),
			VK::VK_MULTIPLY => Key::Character("*".into()),
			VK::VK_ADD => Key::Character("+".into()),
			VK::VK_SEPARATOR => Key::Character("|".into()),
			VK::VK_SUBTRACT => Key::Character("-".into()),
			VK::VK_DECIMAL => Key::Character(".".into()),
			VK::VK_DIVIDE => Key::Character("/".into()),

			VK::VK_F1 => Key::F1,
			VK::VK_F2 => Key::F2,
			VK::VK_F3 => Key::F3,
			VK::VK_F4 => Key::F4,
			VK::VK_F5 => Key::F5,
			VK::VK_F6 => Key::F6,
			VK::VK_F7 => Key::F7,
			VK::VK_F8 => Key::F8,
			VK::VK_F9 => Key::F9,
			VK::VK_F10 => Key::F10,
			VK::VK_F11 => Key::F11,
			VK::VK_F12 => Key::F12,
			VK::VK_F13 => Key::F13,
			VK::VK_F14 => Key::F14,
			VK::VK_F15 => Key::F15,
			VK::VK_F16 => Key::F16,
			VK::VK_F17 => Key::F17,
			VK::VK_F18 => Key::F18,
			VK::VK_F19 => Key::F19,
			VK::VK_F20 => Key::F20,
			VK::VK_F21 => Key::F21,
			VK::VK_F22 => Key::F22,
			VK::VK_F23 => Key::F23,
			VK::VK_F24 => Key::F24,
			VK::VK_NAVIGATION_VIEW => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_MENU => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_UP => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_DOWN => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_LEFT => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_RIGHT => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_ACCEPT => Key::Unidentified(vkey),
			VK::VK_NAVIGATION_CANCEL => Key::Unidentified(vkey),
			VK::VK_NUMLOCK => Key::NumLock,
			VK::VK_SCROLL => Key::ScrollLock,
			VK::VK_OEM_NEC_EQUAL => Key::Unidentified(vkey),
			// VK::VK_OEM_FJ_JISHO => Key::Unidentified(vkey), // Conflicts with `VK::VK_OEM_NEC_EQUAL`
			VK::VK_OEM_FJ_MASSHOU => Key::Unidentified(vkey),
			VK::VK_OEM_FJ_TOUROKU => Key::Unidentified(vkey),
			VK::VK_OEM_FJ_LOYA => Key::Unidentified(vkey),
			VK::VK_OEM_FJ_ROYA => Key::Unidentified(vkey),
			VK::VK_LSHIFT => Key::Shift,
			VK::VK_RSHIFT => Key::Shift,
			VK::VK_LCONTROL => Key::Control,
			VK::VK_RCONTROL => Key::Control,
			VK::VK_LMENU => Key::Alt,
			VK::VK_RMENU => Key::Alt,
			VK::VK_BROWSER_BACK => Key::BrowserBack,
			VK::VK_BROWSER_FORWARD => Key::BrowserForward,
			VK::VK_BROWSER_REFRESH => Key::BrowserRefresh,
			VK::VK_BROWSER_STOP => Key::BrowserStop,
			VK::VK_BROWSER_SEARCH => Key::BrowserSearch,
			VK::VK_BROWSER_FAVORITES => Key::BrowserFavorites,
			VK::VK_BROWSER_HOME => Key::BrowserHome,
			VK::VK_VOLUME_MUTE => Key::AudioVolumeMute,
			VK::VK_VOLUME_DOWN => Key::AudioVolumeDown,
			VK::VK_VOLUME_UP => Key::AudioVolumeUp,
			VK::VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext,
			VK::VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious,
			VK::VK_MEDIA_STOP => Key::MediaStop,
			VK::VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause,
			VK::VK_LAUNCH_MAIL => Key::LaunchMail,
			VK::VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer,
			VK::VK_LAUNCH_APP1 => Key::LaunchApplication1,
			VK::VK_LAUNCH_APP2 => Key::LaunchApplication2,

			// This function only converts "non-printable"
			VK::VK_OEM_1 => Key::Unidentified(vkey),
			VK::VK_OEM_PLUS => Key::Unidentified(vkey),
			VK::VK_OEM_COMMA => Key::Unidentified(vkey),
			VK::VK_OEM_MINUS => Key::Unidentified(vkey),
			VK::VK_OEM_PERIOD => Key::Unidentified(vkey),
			VK::VK_OEM_2 => Key::Unidentified(vkey),
			VK::VK_OEM_3 => Key::Unidentified(vkey),

			VK::VK_GAMEPAD_A => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_B => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_X => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_Y => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_DPAD_UP => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_MENU => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_VIEW => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(vkey),
			VK::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(vkey),

			// This function only converts "non-printable"
			VK::VK_OEM_4 => Key::Unidentified(vkey),
			VK::VK_OEM_5 => Key::Unidentified(vkey),
			VK::VK_OEM_6 => Key::Unidentified(vkey),
			VK::VK_OEM_7 => Key::Unidentified(vkey),
			VK::VK_OEM_8 => Key::Unidentified(vkey),
			VK::VK_OEM_AX => Key::Unidentified(vkey),
			VK::VK_OEM_102 => Key::Unidentified(vkey),

			VK::VK_ICO_HELP => Key::Unidentified(vkey),
			VK::VK_ICO_00 => Key::Unidentified(vkey),

			VK::VK_PROCESSKEY => Key::Process,

			VK::VK_ICO_CLEAR => Key::Unidentified(vkey),
			VK::VK_PACKET => Key::Unidentified(vkey),
			VK::VK_OEM_RESET => Key::Unidentified(vkey),
			VK::VK_OEM_JUMP => Key::Unidentified(vkey),
			VK::VK_OEM_PA1 => Key::Unidentified(vkey),
			VK::VK_OEM_PA2 => Key::Unidentified(vkey),
			VK::VK_OEM_PA3 => Key::Unidentified(vkey),
			VK::VK_OEM_WSCTRL => Key::Unidentified(vkey),
			VK::VK_OEM_CUSEL => Key::Unidentified(vkey),

			VK::VK_OEM_ATTN => Key::Attn,
			VK::VK_OEM_FINISH => {
				if is_japanese {
					Key::Katakana
				} else {
					// This matches IE and Firefox behaviour according to
					// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
					// At the time of writing, there is no `NamedKey::Finish` variant as
					// Finish is not mentioned at https://w3c.github.io/uievents-key/
					// Also see: https://github.com/pyfisch/keyboard-types/issues/9
					Key::Unidentified(vkey)
				}
			}
			VK::VK_OEM_COPY => Key::Copy,
			VK::VK_OEM_AUTO => Key::Hankaku,
			VK::VK_OEM_ENLW => Key::Zenkaku,
			VK::VK_OEM_BACKTAB => Key::Romaji,
			VK::VK_ATTN => Key::KanaMode,
			VK::VK_CRSEL => Key::CrSel,
			VK::VK_EXSEL => Key::ExSel,
			VK::VK_EREOF => Key::EraseEof,
			VK::VK_PLAY => Key::Play,
			VK::VK_ZOOM => Key::ZoomToggle,
			VK::VK_NONAME => Key::Unidentified(vkey),
			VK::VK_PA1 => Key::Unidentified(vkey),
			VK::VK_OEM_CLEAR => Key::Clear,
			_ => Key::Unidentified(vkey),
		}
		// types::Key::Unidentified
	}
}
use conv_key::*;
